/*
 * elf.c - Mokri
 * Contributors: xMine
 * (Any new Contributor may add himself/herself to the list)
 * Changes to be made:
 * Source: ELF Spezifikation
 */

#include "elf.h"

extern void elf_entry ();

/* Lädt eine ELF Datei in den Speicher und richtet diese ein
 * Parameter: Pfad zur ELF Datei
 * Rückgaben: Prozess ID
 */
pid_t elf_load (const char *path) {
	FILE *h = fopen (path, "r"); // ELF Datei öffnen
	
	#ifdef DEBUG_ELF
	printf ("ELF: Lade '%s'\n", path);
	#endif
	
	if (h == NULL) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Konnte Datei nicht oeffnen\n");
		#endif
		return 0;
	}
	
	fseek (h, 0, SEEK_END); // Ans Ende der Datei gehen
	size_t filesize = (size_t)ftell (h);
	fseek (h, 0, SEEK_SET);
	
	char *elf = (char*)malloc (filesize+1); // Speicher holen
	
	if (elf == NULL) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Konnte keinen Speicher holen\n");
		#endif
		return 0;
	}
	
	size_t gelesen;
	if ( (gelesen = fread (elf, filesize, 1, h)) != filesize) { // Elf in den RAM laden
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: fread - wollte %u Bytes, bekam aber %u Bytes\n", filesize, gelesen);
		#endif
		return 0;
	}
	
	fclose (h); // Den Dateihandle brauchen wir nicht mehr
	
	Elf32_Ehdr_t *elf_header = (Elf32_Ehdr_t*)elf;
	
	// Magic überprüfen ( 0x7F, 'E', 'L', 'F' )
	if (elf_header->e_ident[EI_MAG0] != 0x7F ||
		elf_header->e_ident[EI_MAG1] != 'E' ||
		elf_header->e_ident[EI_MAG2] != 'L' ||
		elf_header->e_ident[EI_MAG3] != 'F'
	) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Magic stimmt nicht\n");
		dump_hex (elf, 16);
		#endif
		return 0;
	}
	
	// Einstellungen überprüfen
	if (elf_header->e_ident[EI_CLASS] != ELF_ARCH_CLASS ||
		elf_header->e_ident[EI_DATA] != ELF_ARCH_DATA ||
		elf_header->e_machine != ELF_ARCH_IDENT ||
		elf_header->e_ident[EI_VERSION] != EV_CURRENT
	) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Elf Datei passt nicht auf die Einstellungen\n");
		#endif
		return 0;
	}
	
	if (elf_header->e_phoff == 0) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Elf Datei enthaelt keinen Program Header\n");
		#endif
		return 0;
	}
	
	if (elf_header->e_shoff == 0) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Elf Datei enthaelt keine Section Header\n");
		#endif
		return 0;
	}
	
	if (elf_header->e_shstrndx == SHN_UNDEF) {
		#ifdef DEBUG_ELF
		printf ("ELF: Fehler: Elf Datei enthaelt keine Section Name Table\n");
		#endif
		return 0;
	}
	
	Elf32_Phdr_t *pheader; // Program Header Table
	Elf32_Phdr_t *code = NULL;
	
	#ifdef DEBUG_ELF
	printf ("ELF: Info: ProgramHeader bei 0x%x len: %u anzahl: %u\n", (uint32_t)elf_header->e_phoff, (uint32_t)elf_header->e_phentsize, (uint32_t)elf_header->e_phnum);
	printf ("SectionHeader bei 0x%x len: %u anzahl: %u\n", (uint32_t)elf_header->e_shoff, (uint32_t)elf_header->e_shentsize, (uint32_t)elf_header->e_shnum);
	#endif
	
	uint32_t i, size = 0;
	for (i=0; i<(uint32_t)elf_header->e_phnum; i++) {
		pheader = (Elf32_Phdr_t*)((uint32_t)elf+(uint32_t)elf_header->e_phoff+size);
		
		if (code == NULL && pheader->p_type == PT_LOAD) {
			code = pheader;
		}
		
		size += (uint32_t)elf_header->e_phentsize;
	}
	
	#ifdef DEBUG_ELF
	printf ("Erstelle Task... ");
	#endif
	struct task *prog = elf_create_task (elf_header, code);
	
	if (prog == NULL) {
		free (elf);
		return 0;
	}
	//print ("OK\n");
	
	return prog->pid;
}

/* Erstellt einen Task aus einer Programm Header Table - NOTIZ: Der Task läuft noch nicht sondern muss noch aktiviert werden!
 * Parameter: Zeiger auf einen Elf Header, Zeiger auf eine Elf32_Phdr_t Struktur
 * Rückgaben: Bei Erfolg Zeiger auf eine Task Struktur, ansonsten NULL
 */
struct task *elf_create_task (Elf32_Ehdr_t *elf, Elf32_Phdr_t *ptr) {
	if (ptr->p_type != PT_LOAD) {
		return NULL;
	}
	
	char *code = (char*)pmm_range_alloc (ptr->p_memsz); // Speicher für den Code holen
	
	memset (code, 0x0, ptr->p_memsz); // Komplett nullen
	memcpy (code, (void*)((uint32_t)elf + (uint32_t)ptr->p_offset), ptr->p_filesz); // Code rüber kopieren
	
	// Task erstellen
	//dump_hex (&elf_entrypoint, 32);
	struct task *t = mt_createThread (&elf_entry, FALSE);

	t->sf->ebx = (uint32_t)ptr->p_vaddr;
	t->paddr = (uint32_t)paging_get_physical_address (code);
	t->vaddr = (uint32_t)ptr->p_vaddr;
	t->size = (uint32_t)ptr->p_memsz;
	
	return t;
}

/* Dies ist der Einsprungspunkt eines ELF Programms.
 * Parameter: Adresse des Einsprungspunkts (Der Parameter wird von elf_entry (siehe elf.asm) auf den Stack gepusht) 
 * Rückgaben: Keine
 */
void elf_entrypoint (int (*entry)(int, char**)) {
	char **argv;
	int argc;
	
	argv = GetArgumentList (GetRunningTaskPID(), &argc); // Parameter holen
	
	int ret = entry (argc, argv);
	
	asm volatile   ("mov $9, %%eax\n"
			"int $162\n"
			:: "b"(ret)
			: "%eax");
	
	
	while (1) { } // Und so lange loopen, bis der Task aufhört zu exestieren
}